package com.wayne.cas.config;

import com.google.common.collect.Multimap;
import com.wayne.cas.login.UsernamePasswordKaptchaAuthenticationHandler;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.CoreAuthenticationUtils;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.authentication.principal.PrincipalNameTransformerUtils;
import org.apereo.cas.authentication.support.password.PasswordEncoderUtils;
import org.apereo.cas.authentication.support.password.PasswordPolicyConfiguration;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.model.core.authentication.PasswordEncoderProperties;
import org.apereo.cas.configuration.model.support.jdbc.QueryJdbcAuthenticationProperties;
import org.apereo.cas.configuration.support.JpaBeans;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

@Configuration
public class UsernamePasswordCaptchaConfig implements AuthenticationEventExecutionPlanConfigurer {

    @Resource
    private ServicesManager servicesManager;

    @Resource
    private CasConfigurationProperties casProperties;

    @Resource
    private PrincipalFactory jdbcPrincipalFactory;

    @Autowired(required = false)
    @Qualifier("queryPasswordPolicyConfiguration")
    private PasswordPolicyConfiguration queryPasswordPolicyConfiguration;

    /**
     * 声明自定义的身份认证处理器
     */
    @Bean("usernamePasswordKaptchaAuthenticationHandler")
    public AuthenticationHandler usernamePasswordKaptchaAuthenticationHandler() {
        CustomQueryJdbcAuthenticationProperties b = new CustomQueryJdbcAuthenticationProperties(casProperties);
        final Multimap<String, Object> attributes = CoreAuthenticationUtils.transformPrincipalAttributesListIntoMultiMap(b.getPrincipalAttributeList());
        final UsernamePasswordKaptchaAuthenticationHandler h = new UsernamePasswordKaptchaAuthenticationHandler(
                b.getName(), servicesManager,
                jdbcPrincipalFactory, b.getOrder(),
                JpaBeans.newDataSource(b), b.getSql(), b.getFieldPassword(),
                b.getFieldExpired(), b.getFieldDisabled(), CollectionUtils.wrap(attributes));

        h.setPasswordEncoder(PasswordEncoderUtils.newPasswordEncoder(b.getPasswordEncoder()));
        h.setPrincipalNameTransformer(PrincipalNameTransformerUtils.newPrincipalNameTransformer(b.getPrincipalTransformation()));

        if (queryPasswordPolicyConfiguration != null) {
            h.setPasswordPolicyConfiguration(queryPasswordPolicyConfiguration);
        }

        h.setPrincipalNameTransformer(PrincipalNameTransformerUtils.newPrincipalNameTransformer(b.getPrincipalTransformation()));

        if (StringUtils.isNotBlank(b.getCredentialCriteria())) {
            h.setCredentialSelectionPredicate(CoreAuthenticationUtils.newCredentialSelectionPredicate(b.getCredentialCriteria()));
        }

        return h;
    }

    @Override
    public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
        plan.registerAuthenticationHandler(usernamePasswordKaptchaAuthenticationHandler());
    }

    /**
     * 自定义JDBC查询配置
     */
    static class CustomQueryJdbcAuthenticationProperties extends QueryJdbcAuthenticationProperties {

        private static final long serialVersionUID = 4872638722398158210L;

        private final CasConfigurationProperties casConfigurationProperties;

        public CustomQueryJdbcAuthenticationProperties(CasConfigurationProperties casConfigurationProperties) {
            this.casConfigurationProperties = casConfigurationProperties;
        }

        @Override
        public String getSql() {
            return casConfigurationProperties.getCustom().getProperties().getOrDefault(
                    "sql",
                    "SELECT * FROM system_user WHERE username =?"
            );
        }

        @Override
        public String getFieldPassword() {
            return casConfigurationProperties.getCustom().getProperties().getOrDefault(
                    "fieldPassword",
                    "password"
            );
        }

        @Override
        public String getUrl() {
            return casConfigurationProperties.getCustom().getProperties().getOrDefault(
                    "url",
                    "jdbc:mysql://localhost:3306/oauth2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false"
            );
        }

        @Override
        public String getDialect() {
            return casConfigurationProperties.getCustom().getProperties().getOrDefault(
                    "dialect",
                    "org.hibernate.dialect.MySQLDialect"
            );
        }

        @Override
        public String getUser() {
            return casConfigurationProperties.getCustom().getProperties().getOrDefault(
                    "user",
                    "root"
            );
        }

        @Override
        public String getPassword() {
            return casConfigurationProperties.getCustom().getProperties().getOrDefault(
                    "password",
                    "123456"
            );
        }

        @Override
        public String getDriverClass() {
            return casConfigurationProperties.getCustom().getProperties().getOrDefault(
                    "driverClass",
                    "com.mysql.jdbc.Driver"
            );
        }

        @Override
        public PasswordEncoderProperties getPasswordEncoder() {
            return new CustomPasswordEncoderProperties(casConfigurationProperties);
        }
    }

    /**
     * 自定义密码加密配置
     */
    static class CustomPasswordEncoderProperties extends PasswordEncoderProperties {

        private static final long serialVersionUID = -4675488743635765725L;

        private final CasConfigurationProperties casConfigurationProperties;

        public CustomPasswordEncoderProperties(CasConfigurationProperties casConfigurationProperties) {
            this.casConfigurationProperties = casConfigurationProperties;
        }

        @Override
        public String getType() {
            return casConfigurationProperties.getCustom().getProperties().getOrDefault(
                    "passwordEncoderType",
                    "BCRYPT"
            );
        }

        @Override
        public String getCharacterEncoding() {
            return casConfigurationProperties.getCustom().getProperties().getOrDefault(
                    "passwordEncoderCharacterEncoding",
                    "UTF-8"
            );
        }
    }
}